# coding: utf-8
# author: t_wolf

from web_utils import *

"""
# 所有filter接收的首个入参都为worker对象
# 通过首个框架类对象可以获取到四个属性
#  header: 可以取到请求头中的键值, obj.header.get("xx")
# session: 可以取到用户session并做操作, obj.session.get("xx") || obj.session.set_default(key, value)
# request: 可以取到请求中的参数字典, obj.request.get("xx")
#   pages: 获取当前可以渲染的页面对象, pages.works.index
#
# pre_filter:
#  return: 过滤器根据返回值来判定,如果过滤通过,必须返回真值
#
# 实例:
# def example(worker, arg_list):
#     print worker.header.get("PATH_INFO")
#     return True
#
"""


def frame_handler():
    pass


class PreFilter:
    def __init__(self):
        pass

    @staticmethod
    @pre_filter
    def req_json_string_to_params(worker, arg_list):
        """
        请求的data数据转换为frame_worker可以访问的变量
        :param worker: frame_worker对象
        :param arg_list: 参数列表
        :return: 返回过滤结果
        """
        logger.debug("Transfer request json data to params.")
        try:
            worker.request.update(json2params(web.data()) if web.data() != "" else {})
        except ValueError:
            logger.error("Wrong Type of Input Data For This Request: %s" % web.data())
            raise web.internalerror("Wrong Type of Input Data. Need Json Format.")

    @staticmethod
    @pre_filter
    def req_params_strip(worker, arg_list):
        """
        对Request中的每一个参数都做strip操作
        :param worker: frame_worker对象
        :param arg_list: 参数列表
        :return: 返回过滤结果
        """
        logger.debug("Get strip all request params.")
        for _key, _value in worker.request.items():
            worker.request[_key] = _value.strip() if isinstance(_value, str) else _value

    @staticmethod
    @pre_filter
    def req_params_encode(worker, code_type):
        """
        对Request中的每一个参数做decode操作
        :param worker: frame_worker对象
        :param code_type: 参数列表
        :return: 返回过滤结果
        """
        code_type = "UTF-8" if code_type is None else code_type
        logger.debug("Get encoded all request params to [%s]." % code_type)
        for _key, _value in worker.request.items():
            worker.request[_key] = _value.encode(code_type) if isinstance(_value, unicode) else _value

    @staticmethod
    @pre_filter
    def require_params_check(worker, arg_list):
        """
        必填项检查
        :param worker: frame_worker对象
        :param arg_list: 需要检查的参数列表
        :return: 返回过滤结果
        """
        logger.debug("Check require params %s" % str(arg_list))
        for arg_name in arg_list:
            if not worker.request_has(arg_name):
                logger.error("Required argument [%s] can not be null." % arg_name)
                raise web.internalerror("Required argument [%s] can not be null." % arg_name)

    @staticmethod
    @pre_filter
    def request_params_type_check(worker, arg_list):
        """
        入参类型检查
        :param worker: frame_worker对象
        :param arg_list: 入参及其类型的dict
        :return: 返回过滤结果
        """
        logger.debug("Check request params type by dict: %s" % str(arg_list))
        for _key, _value in arg_list.items():
            if not isinstance(worker.request_get(_key), _value):
                logger.error("Argument [%s] not valid. Expect is %s" % (_key, str(_value)))
                raise web.internalerror("Argument [%s] not valid. Expect is %s" % (_key, str(_value)))

    @staticmethod
    @pre_filter
    def request_number_check_range(worker, arg_dict):
        """
        检查为number类型的入参范围
        :param worker: frame_worker对象
        :param arg_dict: 入参范围字典
        :return: 返回过滤结果
        """
        logger.debug("Check request params number ranges valid: %s" % str(arg_dict))
        for _key, _value in arg_dict.items():
            if worker.request_has(_key) and (worker.request_get(_key) in _value):
                return True
            else:
                logger.error("Argument [%s] not in valid range %s." % (_key, str(_value)))
                raise web.internalerror("Argument [%s] not in valid range %s." % (_key, str(_value)))

    @staticmethod
    @pre_filter
    def save_upload_file(worker, arg_dict):
        # TODO 超大文件上传如何处理
        """
        保存上传文件到指定目录
        :param worker: frame_worker对象
        :param arg_dict: 入参字典save_path, absolute, wrapper, unique
        :return: 返回过滤结果
        """
        var_name = arg_dict.get('var_name', None)
        save_path = arg_dict.get('save_path', None)
        path_absolute = arg_dict.get('absolute', False)

        if not var_name:
            raise web.internalerror("Which variable to get upload content is not notify!")

        if save_path:
            save_path = save_path if path_absolute else os.path.join(worker.path, save_path)
            if not os.path.isdir(save_path):
                raise web.internalerror("Target path to save upload file is not a directory!")
        else:
            raise web.internalerror("Which directory to save upload file is not notify!")

        for _var in web.ctx._fieldstorage.value:
            if _var.name == var_name:
                origin_file = _var
                break
        else:
            raise web.internalerror("There was no file uploads.")

        origin_var = origin_file.name
        origin_name = origin_file.filename
        origin_name = origin_name.decode("UTF-8")

        file_name = 'upload-%d-%d'.decode("UTF-8") % (int(time.time()), random.randint(1000, 9999)) if arg_dict.get(
            'wrapper', True) else origin_name

        file_save = os.path.join(save_path, file_name)
        file_unique = arg_dict.get('unique', False)

        if os.path.exists(file_save):
            if file_unique:
                open(file_save, 'w')
            else:
                file_name += "_new_%d%d" % (int(time.time()), random.randint(1000, 9999))

        file_save = os.path.join(save_path, file_name)
        file_size = web.utils.intget(web.ctx.env.get('CONTENT_LENGTH'), 0)

        recv_file = StringIO.StringIO(origin_file.value)

        step = 1024 * 1024
        while 0 < file_size:
            if file_size - step <= 0:
                step = file_size

            _buff = recv_file.read(step)
            with open(file_save, 'ab') as _f:
                _f.write(_buff)

            file_size = file_size - step

        logger.debug("Save upload file %s to %s." % (origin_name, file_save))

        worker.request.update({
            'upload_file_name': file_name,
            'upload_file_path': file_save,
            'upload_origin_var': origin_var,
            'upload_origin_name': origin_name
        })

        return True
